import os
import subprocess
import tkinter as tk
import tkinter.messagebox
from tkinter import ttk
import json
import sys
from ttkbootstrap import Style

style = Style(theme='cosmo')
default_arg = {
    'character': 'lambda_00',
    'emotions':'默认表情-勿删',
    'breathe':0,
    'frequency':0,
    'input': 2,
    'output': 2,
    'ifm': None,
    'osf': '127.0.0.1:11573',
    'is_extend_movement': False,
    'is_anime4k': False,
    'is_alpha_split': False,
    'is_bongo': False,
    'is_eyebrow': False,
    'cache_simplify': 1,
    'cache_size': 1,
    'model_type': 0,
    'style':"cosmo"
}

try:
    f = open('launcher.json')
    args = json.load(f)
    default_arg.update(args)
    f.close()
except:
    pass
finally:
    args = default_arg
print("这个窗口是程序主窗口，请勿关闭")
p = None
facecap=None
S_keys_input = ""
dirPath = 'data/character'
emotionsdirPath = 'data/emotions'
modulePath = 'data\models'
is_4k = 0
characterList = []
for item in sorted(os.listdir(dirPath), key=lambda x: -os.path.getmtime(os.path.join(dirPath, x))):
    if '.model' == item[-6:]:
        characterList.append(item[:-6])
emotionsList = []
for item in sorted(os.listdir(emotionsdirPath), key=lambda x: -os.path.getmtime(os.path.join(emotionsdirPath, x))):
    if '.json' == item[-5:]:
        emotionsList.append(item[:-5])

for item in sorted(os.listdir(modulePath), key=lambda x: -os.path.getmtime(os.path.join(modulePath, x))):
    if 'anime4k.model' == item:
        is_4k = 1
        
root = style.master
#root = tk.Tk()
root.resizable(False, False)
root.geometry('380x780+100+100')
root.title('Launcher')



theme_names = style.theme_names()#以列表的形式返回多个主题名
theme_selection = ttk.Frame(root, padding=(10, 10, 10, 0))
theme_selection.pack(fill='x', expand='YES')
lbl = ttk.Label(theme_selection, text="窗口主题:")
theme_cbo = ttk.Combobox(
        master=theme_selection,
        text=style.theme.name,
        values=theme_names,
)
theme_cbo.pack(padx=10, side='right')
style_name = args['style']
theme_cbo.current(theme_names.index(style_name))

theme_cbo_value = style_name
style.theme_use(theme_cbo_value)
theme_cbo.selection_clear()
    
lbl.pack(side='right')
def change_theme(event):
    theme_cbo_value = theme_cbo.get()
    style.theme_use(theme_cbo_value)
    theme_selected.configure(text="大鹅猫")
    theme_cbo.selection_clear()
theme_cbo.bind('<<ComboboxSelected>>', change_theme)


theme_selected = ttk.Label(
        master=theme_selection,
        text="大鹅猫",
        font="-size 24 -weight bold"
)
theme_selected.pack(side='left')








launcher = ttk.Frame(root)
launcher.pack(fill='x', expand=True)


def launch():
    global p
    global launch_btn
    if p is not None:
        subprocess.run(['taskkill', '/F', '/PID', str(p.pid), '/T'], stdout=subprocess.DEVNULL,
                       stderr=subprocess.DEVNULL)
        p = None
        launch_btn.config(text="开始运行live2d")

    else:
        args = {
            'character': character.get(),
            'emotions':emotions.get(),
            'breathe':is_breathe.get(),
            'frequency':scale.get(),
            'input': input.get(),
            'output': output.get(),
            'ifm': ifm.get(),
            'osf': osf.get(),
            'is_extend_movement': is_extend_movement.get(),
            'is_anime4k': is_anime4k.get(),
            'is_alpha_split': is_alpha_split.get(),
            'is_bongo': is_bongo.get(),
            'is_eyebrow': is_eyebrow.get(),
            'cache_simplify': cache_simplify.get(),
            'cache_size': cache_size.get(),
            'model_type': model_type.get(),
            'style': theme_cbo.get()
        }

        if args['input'] == 0:
            if len(args['ifm']) == 0:
                tkinter.messagebox.showinfo('EasyVtuber Launcher', 'Please Input iFacialMocap IP:Port')
                return
        if args['input'] == 4:
            if len(args['osf']) == 0:
                tkinter.messagebox.showinfo('EasyVtuber Launcher', 'Please Input OpenSeeFace IP:Port')
                return

        f = open('launcher.json', mode='w')
        json.dump(args, f)
        f.close()

#        run_args = [sys.executable, 'main.py']
        run_args = ['bin/python.exe','main.py']
        if len(args['character']):
            run_args.append('--character')
            run_args.append(args['character'])

        if args['input'] == 0:
            if len(args['ifm']):
                run_args.append('--ifm')
                run_args.append(args['ifm'])
        elif args['input'] == 1:
            run_args.append('--input')
            run_args.append('cam')
        elif args['input'] == 2:
            run_args.append('--debug_input')
        elif args['input'] == 3:
            run_args.append('--mouse_input')
            run_args.append('0,0,' + str(root.winfo_screenwidth()) + ',' + str(root.winfo_screenheight()))
        elif args['input'] == 4:
            if len(args['osf']):
                run_args.append('--osf')
                run_args.append(args['osf'])

        if args['output'] == 0:
            run_args.append('--output_webcam')
            run_args.append('unitycapture')
        elif args['output'] == 1:
            run_args.append('--output_webcam')
            run_args.append('obs')
        elif args['output'] == 2:
            run_args.append('--debug')
        if args['is_anime4k']:
            run_args.append('--anime4k')
        if args['is_alpha_split']:
            run_args.append('--alpha_split')
        if args['is_extend_movement']:
            run_args.append('--extend_movement')
            run_args.append('1')
        if args['is_bongo']:
            run_args.append('--bongo')
        if args['is_eyebrow']:
            run_args.append('--eyebrow')
        if args['cache_simplify'] is not None:
            run_args.append('--simplify')
            run_args.append(str(args['cache_simplify']))
        if args['cache_size'] is not None:
            run_args.append('--cache')
            run_args.append(['0b', '256mb', '1gb', '2gb', '4gb', '8gb'][args['cache_size']])
            run_args.append('--gpu_cache')
            run_args.append(['0b', '128mb', '512mb', '1gb', '2gb', '4gb'][args['cache_size']])
        if args['model_type'] is not None:
            run_args.append('--model')
            model_name = ['standard_float', 'standard_half', 'separable_half'][args['model_type']]
            run_args.append(model_name)
            if not os.path.exists('data/models/' + model_name + '/face_morpher.pt'):
                tkinter.messagebox.showinfo('EasyVtuber Launcher',
                                            'Missing Model File: ' + 'data/models/' + model_name + '\nCheck link 00B or README.md for more info.')
                return

        run_args.append('--output_size')
        run_args.append('512x512')
        print('Launched: ' + ' '.join(run_args))
        p = subprocess.Popen(run_args,shell=False,creationflags=0x08000000)
        launch_btn.config(text='中止')

def open_facial_capture():
    global facecap
    if facecap is not None:
        subprocess.run(['taskkill', '/F', '/PID', str(facecap.pid), '/T'], stdout=subprocess.DEVNULL,
                       stderr=subprocess.DEVNULL)
        facecap = None
        facial_capture_btn.config(text="开启面捕")
    else:
        subprocess.Popen('cmd.exe /c "echo off & call start openosf.bat"',creationflags=0x08000000)
        facial_capture_btn.config(text="关闭黑框以中止面捕") 
    
def open_manual_poser():
    stop_manual_poser = subprocess.Popen('cmd.exe /c "echo off & call start open_manual_poser.vbs"',creationflags=0x08000000)
def open_png():
    start_directory = 'data\character'
    os.system("explorer.exe %s" % start_directory)
def open_emotions():
    start_directory = 'data'
    os.system("explorer.exe %s" % start_directory)
    
open_png_btn = ttk.Button(launcher, text="打开人设文件夹",command=open_png)
open_png_btn.pack(side='bottom', fill='x', expand=True,  pady=5, padx=5)

open_emotions_btn = ttk.Button(launcher, text="打开表情文件夹", command=open_emotions)
open_emotions_btn.pack(side='bottom', fill='x', expand=True,  pady=5, padx=5)

emotions_btn = ttk.Button(launcher, text="添加表情", command=open_manual_poser)
emotions_btn.pack(side='bottom', fill='x', expand=True,  pady=5, padx=5)

facial_capture_btn = ttk.Button(launcher, text="开启面捕", command=open_facial_capture)
facial_capture_btn.pack(side='bottom', fill='x', expand=True, pady=5, padx=5)

launch_btn = ttk.Button(launcher, text="保存并开始运行live2d", command=launch)
launch_btn.pack(side='bottom', fill='x', expand=True,  pady=5, padx=5)

frameL = ttk.Frame(launcher)
frameL.pack(padx=10, pady=10, fill='both', side='left', expand=True)
frameR = ttk.Frame(launcher)
frameR.pack(padx=10, pady=10, fill='both', side='left', expand=True)

character = tk.StringVar(value=args['character'])
ttk.Label(frameL, text="角色列表").pack(fill='x', expand=True)
# ttk.Entry(frameL, textvariable=character).pack(fill='x', expand=True)
char_combo = ttk.Combobox(frameL, textvariable=character, value=characterList)
char_combo.pack(fill='x', expand=True)


if args['emotions'] not in emotionsList:
    args['emotions'] = '默认表情-勿删'
def on_select(event):
    S_keys_input = event.widget.get()
emotions = tk.StringVar(value=args['emotions'])
ttk.Label(frameL, text="表情列表").pack(fill='x', expand=True)
emotions_combo = ttk.Combobox(frameL, textvariable=emotions, value=emotionsList)
emotions_combo.pack(fill='x', expand=True)
emotions_combo.bind("<<ComboboxSelected>>", on_select)
#launcher1 = ttk.Frame(root)
#launcher1.pack(fill='x', side='top',expand=True)
#scale = tk.Scale(launcher1, from_=0,to=1,resolution=0.1,orient=tk.HORIZONTAL, showvalue=1)
#scale.pack(side=tk.LEFT)


def inputChange():
    i=input.get()
    if i==0:
        ifmLbl.pack(fill='x', expand=True)
        ifmEnt.pack(fill='x', expand=True)
        osfLbl.pack_forget()
        osfEnt.pack_forget()
    elif i==4:
        ifmLbl.pack_forget()
        ifmEnt.pack_forget()
        osfLbl.pack(fill='x', expand=True)
        osfEnt.pack(fill='x', expand=True)
    else:
        ifmLbl.pack_forget()
        ifmEnt.pack_forget()
        osfLbl.pack_forget()
        osfEnt.pack_forget()
        frameLTxt.configure(height=0)
        
def select(a):
    with open("launcher.json", "r", encoding="utf-8") as f:
        launcher = json.load(f)
    launcher['frequency'] = a
    with open('launcher.json', 'w') as f:
        json.dump(launcher, f)
        
'''
已废除
def breatheChange():
    i=is_breathe.get()
    if i==0:
        breathe_label.pack(fill='x', expand=True)
        scale.pack(fill='x', expand=True)
    else:
        breathe_label.pack_forget()
        scale.pack_forget()

ttk.Radiobutton(frameL, text='呼吸-需要删除', value=0, variable=is_breathe, command=breatheChange).pack(fill='x', expand=True)
ttk.Radiobutton(frameL, text='不呼吸-需要删除', value=1, variable=is_breathe, command=breatheChange).pack(fill='x', expand=True)
'''
is_breathe = tk.IntVar(value=args['breathe'])
frameLTxt = ttk.Frame(frameL)
frameLTxt.pack(fill='x', expand=True)
Shortcut_keys = ttk.Label(frameLTxt, text="此表情的快捷键：")
Shortcut_keys.pack(fill='x', expand=True)

breathe_label = ttk.Label(frameL, text="呼吸频率：ctrl+0开启/关闭")
breathe_label.pack(fill='x', expand=True)
scale = tk.Scale(frameL, from_=0,to=1,resolution=0.1,orient=tk.HORIZONTAL, showvalue=1,command=select)
scale.set(args['frequency'])
scale.pack(fill='x', expand=True)


input = tk.IntVar(value=args['input'])
ttk.Label(frameL, text="面捕源").pack(fill='x', expand=True)
ttk.Radiobutton(frameL, text='iFacialMocap', value=0, variable=input, command=inputChange).pack(fill='x', expand=True)
ttk.Radiobutton(frameL, text='OpenSeeFace', value=4, variable=input, command=inputChange).pack(fill='x', expand=True)
ttk.Radiobutton(frameL, text='Webcam(opencv)', value=1, variable=input, command=inputChange).pack(fill='x', expand=True)
ttk.Radiobutton(frameL, text='跟随鼠标', value=3, variable=input, command=inputChange).pack(fill='x', expand=True)
ttk.Radiobutton(frameL, text='调试模式', value=2, variable=input, command=inputChange).pack(fill='x',
                                                                                                       expand=True)



def getemotion(path):
    with open("data\emotions\\"+path+".json", "r", encoding="utf-8") as f:
        launcher = json.load(f)
    return launcher
def writejson(path , datajson):
    with open("data\emotions\\"+path+".json", 'w') as f:
        json.dump(datajson, f)

pressed_keys = []
save_keys = set()

def printkey(event):
    global entry
    global pressed_keys,save_keys
    if event.keysym not in pressed_keys:
        pressed_keys.append(event.keysym)
        save_keys.add(str(event.keycode))
    entry.delete(0, tk.END)
    entry.insert(0, str("+".join(pressed_keys).replace("??+","").replace("??","请使用英文输入法")))
    datajson = getemotion(emotions.get())
    datajson['Shortcut keys'] = str("+".join(pressed_keys).replace("??+","").replace("??",""))
    datajson['save_keys'] = str("+".join(sorted(list(save_keys))).replace("??+","").replace("??",""))
    writejson(emotions.get(),datajson)
def releasekey(event):
	global pressed_keys,save_keys
	if event.keysym in pressed_keys:
		pressed_keys.remove(event.keysym)
		save_keys.remove(str(event.keycode))
	# print("你松开了：", "+".join(pressed_keys))

# 创建一个输入框
entry = ttk.Entry(frameLTxt, textvariable=args['emotions'])
entry.delete(0, tk.END)
entry.insert(0, getemotion(emotions.get())["Shortcut keys"])
entry.pack()
entry.bind("<KeyPress>", printkey)
entry.bind("<KeyRelease>", releasekey)
# 定义一个函数来更新输入框的值
def update_entry(*args):
    entry.delete(0, tk.END)
    entry.insert(0, getemotion(emotions.get())["Shortcut keys"])

# 监视selected_value的更改
emotions.trace('w', update_entry)

ifmLbl = ttk.Label(frameLTxt, text="iFacialMocap IP:Port")
ifmLbl.pack(fill='x', expand=True)

ifm = tk.StringVar(value=args['ifm'])
ifmEnt = ttk.Entry(frameLTxt, textvariable=ifm, state=False)
ifmEnt.pack(fill='x', expand=True)

osfLbl = ttk.Label(frameLTxt, text="OpenSeeFace IP:Port")
osfLbl.pack(fill='x', expand=True)
osf = tk.StringVar(value=args['osf'])
osfEnt = ttk.Entry(frameLTxt, textvariable=osf, state=False)
osfEnt.pack(fill='x', expand=True)
inputChange()

ttk.Label(frameR, text="简化模型：").pack(fill='x', expand=True)
model_type = tk.IntVar(value=args['model_type'])
ttk.Radiobutton(frameR, text='Off', value=0, variable=model_type).pack(fill='x', expand=True)
ttk.Radiobutton(frameR, text='Low', value=1, variable=model_type).pack(fill='x', expand=True)
ttk.Radiobutton(frameR, text='High', value=2, variable=model_type).pack(fill='x', expand=True)

ttk.Label(frameR, text="简化面捕信息：").pack(fill='x', expand=True)
cache_simplify = tk.IntVar(value=args['cache_simplify'])
ttk.Radiobutton(frameR, text='Off', value=0, variable=cache_simplify).pack(fill='x', expand=True)
ttk.Radiobutton(frameR, text='Low', value=1, variable=cache_simplify).pack(fill='x', expand=True)
ttk.Radiobutton(frameR, text='Medium', value=2, variable=cache_simplify).pack(fill='x', expand=True)
ttk.Radiobutton(frameR, text='High', value=3, variable=cache_simplify).pack(fill='x', expand=True)
ttk.Radiobutton(frameR, text='Higher', value=4, variable=cache_simplify).pack(fill='x', expand=True)
ttk.Radiobutton(frameR, text='Highest', value=6, variable=cache_simplify).pack(fill='x', expand=True)
ttk.Radiobutton(frameR, text='Gaming', value=8, variable=cache_simplify).pack(fill='x', expand=True)

ttk.Label(frameR, text="可使用的内存+显存").pack(fill='x', expand=True)
cache_size = tk.IntVar(value=args['cache_size'])
ttk.Radiobutton(frameR, text='Off', value=0, variable=cache_size).pack(fill='x', expand=True)
ttk.Radiobutton(frameR, text='256M+128M', value=1, variable=cache_size).pack(fill='x', expand=True)
ttk.Radiobutton(frameR, text='1GB+512M', value=2, variable=cache_size).pack(fill='x', expand=True)
ttk.Radiobutton(frameR, text='2GB+1GB', value=3, variable=cache_size).pack(fill='x', expand=True)
ttk.Radiobutton(frameR, text='4GB+2GB', value=4, variable=cache_size).pack(fill='x', expand=True)
ttk.Radiobutton(frameR, text='8GB+4GB', value=5, variable=cache_size).pack(fill='x', expand=True)

ttk.Label(frameL, text="附加选项").pack(fill='x', expand=True)
is_eyebrow = tk.BooleanVar(value=args['is_eyebrow'])
ttk.Checkbutton(frameL, text='眉毛捕捉', variable=is_eyebrow).pack(fill='x', expand=True)

is_extend_movement = tk.BooleanVar(value=args['is_extend_movement'])
ttk.Checkbutton(frameL, text='更大的活动范围', variable=is_extend_movement).pack(fill='x', expand=True)

if is_4k == 1:
	is_anime4k = tk.BooleanVar(value=args['is_anime4k'])
	ttk.Checkbutton(frameL, text='4K超清', variable=is_anime4k).pack(fill='x', expand=True)
else:
    is_anime4k = tk.BooleanVar(value=False)

is_alpha_split = tk.BooleanVar(value=args['is_alpha_split'])
ttk.Checkbutton(frameL, text='透明输出-直播用', variable=is_alpha_split).pack(fill='x', expand=True)

is_bongo = tk.BooleanVar(value=args['is_bongo'])
ttk.Checkbutton(frameL, text='bongo猫模式(倾斜)', variable=is_bongo).pack(fill='x', expand=True)

output = tk.IntVar(value=args['output'])
ttk.Label(frameR, text="输出").pack(fill='x', expand=True)
ttk.Radiobutton(frameR, text='Unity Capture', value=0, variable=output).pack(fill='x', expand=True)
ttk.Radiobutton(frameR, text='OBS Virtual Camera', value=1, variable=output).pack(fill='x', expand=True)
ttk.Radiobutton(frameR, text='调试输出', value=2, variable=output).pack(fill='x', expand=True)


def closeWindow():
    if p is not None:
        subprocess.run(['taskkill', '/F', '/PID', str(p.pid), '/T'], stdout=subprocess.DEVNULL,
                       stderr=subprocess.DEVNULL)
    root.destroy()

def handle_focus_emotions(event):
    characterList = []
    if event.widget == root:
        for item in sorted(os.listdir(dirPath), key=lambda x: -os.path.getmtime(os.path.join(dirPath, x))):
            if '.model' == item[-6:]:
                characterList.append(item[:-6])
        char_combo.config(value=characterList)
    emotionsList = []
    if event.widget == root:
        for item in sorted(os.listdir(emotionsdirPath), key=lambda x: -os.path.getmtime(os.path.join(emotionsdirPath, x))):
            if '.json' == item[-5:]:
                emotionsList.append(item[:-5])
        emotions_combo.config(value=emotionsList)
root.resizable(True, True)
root.bind("<FocusIn>", handle_focus_emotions)
root.protocol('WM_DELETE_WINDOW', closeWindow)
# root.iconphoto(False, tk.PhotoImage(file='daemao.png'))#更改启动器图标的
root.mainloop()
